using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using SharpMap.Geometries;
using SharpMap.Presentation.Views;
using SharpMap.Rendering.Rendering2D;
using SharpMap.Rendering.Thematics;
using SharpMap.Styles;
using SharpMap.Wpf.Rendering;
using GeoPoint = SharpMap.Geometries.Point;
using WpfRect = System.Windows.Rect;
using System.ComponentModel;
namespace SharpMap.Presentation.Wpf
{

    /// <summary>
    /// This is the most basic facility to present a map.
    /// it will present the map given in Map property.
    /// </summary>
    public class MapView :  FrameworkElement
        , IMapView2D
    {

        //-----------------------------------------------------
        //
        //  Dependency Properties
        //
        //-----------------------------------------------------

        #region Dependency Properties
        
        public static ITheme GetTheme(DependencyObject obj)
        {
            return (ITheme)obj.GetValue(ThemeProperty);
        }

        public static void SetTheme(DependencyObject obj, ITheme value)
        {
            obj.SetValue(ThemeProperty, value);
        }

        public static readonly DependencyProperty ThemeProperty =
            DependencyProperty.RegisterAttached("Theme", typeof(ITheme), typeof(MapView)
            , new FrameworkPropertyMetadata(
                default(ITheme)));

        public ITheme Theme
        {
            get { return GetTheme(this); }
            set { SetTheme(this, value); }
        }

        public static readonly DependencyProperty GeoCenterProperty =
            DependencyProperty.Register("GeoCenter", typeof(GeoPoint), 
                typeof(MapView),
                    new FrameworkPropertyMetadata(new GeoPoint())
                );

        public GeoPoint GeoCenter
        {
            get { return (GeoPoint)GetValue(GeoCenterProperty); }
            set { SetValue(GeoCenterProperty, value); }
        }

        public Matrix2D ToViewTransform
        {
            get { return (Matrix2D)GetValue(ToViewTransformProperty); }
        }
        public static readonly DependencyProperty ToViewTransformProperty =
            DependencyProperty.Register("ToViewTransform", typeof(Matrix2D), typeof(MapView), new UIPropertyMetadata(default(Matrix2D)));

  
        public Size2D ViewSize
        {
            get { return GetViewSize(this); }
            set { SetViewSize(this, value); }
        }

        public static Size2D GetViewSize(DependencyObject obj)
        {
            return (Size2D)obj.GetValue(ViewSizeProperty);
        }

        public static void SetViewSize(DependencyObject obj, Size2D value)
        {
            obj.SetValue(ViewSizeProperty, value);
        }

        public static readonly DependencyProperty ViewSizeProperty =
            DependencyProperty.Register("ViewSize", typeof(Size2D), typeof(MapView)
            , new FrameworkPropertyMetadata(default(Size2D)
             , ViewSizePropertyChanged));

        static void ViewSizePropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            MapView This = target as MapView;
            if (This != null)
            {
                   This.raiseSizeChanged();
            }
        }



        public BoundingBox ViewEnvelope
        {
            get { return (BoundingBox)GetValue(ViewEnvelopeProperty); }
            set { SetValue(ViewEnvelopeProperty, value); }
        }
        public static readonly DependencyProperty ViewEnvelopeProperty =
            DependencyProperty.Register("ViewEnvelope", typeof(BoundingBox), typeof(MapView), new UIPropertyMetadata(default(BoundingBox)));


        public Map Map
        {
            get { return (Map)GetValue(MapProperty); }
            set { SetValue(MapProperty, value); }
        }
        
        public static readonly DependencyProperty MapProperty =
            DependencyProperty.Register("Map", typeof(Map), typeof(MapView),
            new FrameworkPropertyMetadata(default(Map)
                ,FrameworkPropertyMetadataOptions.AffectsRender
                ,MapPropertyChanged));

        static void MapPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            MapView This = target as MapView;
            Map newMap = e.NewValue as Map;
            if (This != null)
            {
                if (newMap != null)
                {
                    This._model = new MapViewModel(newMap, This);
                    This._model.Theme = This.Theme; 
                    This.DataContext = This._model;
                    This.BackgroundRect.DataContext = This._model;
                    This.RenderedFeaturesLayer.DataContext = This._model.RenderedObjects;

                    This.SelectedFeaturesLayer.DataContext = This._model.RenderedSelectedObjects;
                    This.HighlightedFeaturesLayer.DataContext = This._model.RenderedHighlightedObjects;

                    This.MapAdornersLayer.DataContext = This._model.RenderedAdorners;
                    This.InvalidateMeasure();
                }
            }
        }



        public double WorldAspectRatio
        {
            get { return (double)GetValue(WorldAspectRatioProperty); }
            set { SetValue(WorldAspectRatioProperty, value); }
        }
        public static readonly DependencyProperty WorldAspectRatioProperty =
            DependencyProperty.Register("WorldAspectRatio", typeof(double), typeof(MapView), new UIPropertyMetadata(default(double)));



        public double MaximumWorldWidth
        {
            get { return (double)GetValue(MaximumWorldWidthProperty); }
            set { SetValue(MaximumWorldWidthProperty, value); }
        }
        public static readonly DependencyProperty MaximumWorldWidthProperty =
            DependencyProperty.Register("MaximumWorldWidth", typeof(double), typeof(MapView), new UIPropertyMetadata(default(double)));

        public double MinimumWorldWidth
        {
            get { return (double)GetValue(MinimumWorldWidthProperty); }
            set { SetValue(MinimumWorldWidthProperty, value); }
        }
        public static readonly DependencyProperty MinimumWorldWidthProperty =
            DependencyProperty.Register("MinimumWorldWidth", typeof(double), typeof(MapView), new UIPropertyMetadata(default(double)));


                                                              
        #endregion //DependencyProperties

        //-----------------------------------------------------
        //
        //  Construction
        //
        //-----------------------------------------------------
 
        #region Construction
        static MapView()
        {
            ClipToBoundsProperty.OverrideMetadata(typeof(MapView)
                , new FrameworkPropertyMetadata(true));

            RegisterCommands();
        }

        public MapView()
        {
            CreateOwnDrawingLayers();
            WireUpBinding();
       }

 
        #endregion

        //-----------------------------------------------------
        //
        //  Public properties 
        // 
        //-----------------------------------------------------

        #region Public Properties
        public bool IsMultiselectEnabled
        {
            get { return isMultiselectEnabled; }
            set { isMultiselectEnabled = value; }
        }
  
        #endregion

        //-----------------------------------------------------
        //
        //  IMapView2D Members
        // 
        //-----------------------------------------------------

        #region IMapView2D Members

        
        #region IMapView2D Events

        // I devide the hadeleing of IMapView2D events into the following categories:
        //  1. Simple Property change requests - these are hadeled using Binding to the MapViewModel,
        //      and hence are not used elwhere. They are explicitly implemnted
        //  2. Action requests - the preferred use is through commands, so they are invoked in 
        //      the command hadlers
        //  3. Tool actions - Hover, BeginAction, MoveTo, EndAction - these are just routed to the 
        //      active tool. Maybe in future, they'll be hadeled using another class, 
        //      which will intercept the low level input events (Mouse, keybord)
        //  3. Property change notification - no special treatment. Just invoked when the property changes

        #region Simple Properties

        event EventHandler<MapViewPropertyChangeEventArgs<StyleColor>>
            IMapView2D.BackgroundColorChangeRequested
        {
            add { _backgroundColorChangeRequestedHandler += value; }
            remove { _backgroundColorChangeRequestedHandler -= value; }
        }

        event EventHandler<MapViewPropertyChangeEventArgs<SharpMap.Geometries.Point>>
            IMapView2D.GeoCenterChangeRequested
        {
            add { _geoCenterChangeRequestedHandler += value; }
            remove { _geoCenterChangeRequestedHandler -= value; }
        }

        event EventHandler<MapViewPropertyChangeEventArgs<Double>>
            IMapView2D.MaximumWorldWidthChangeRequested
        {
            add { _maximumWorldWidthChangeRequestedHandler += value; }
            remove { _maximumWorldWidthChangeRequestedHandler -= value; }
        }

        event EventHandler<MapViewPropertyChangeEventArgs<Double>>
               IMapView2D.MinimumWorldWidthChangeRequested
        {
            add { _minimumWorldWidthChangeRequestedHandler += value; }
            remove { _minimumWorldWidthChangeRequestedHandler -= value; }
        }

        event EventHandler<MapViewPropertyChangeEventArgs<BoundingBox>>
        IMapView2D.ViewEnvelopeChangeRequested
        {
            add { _viewEnvelopeChangedRequestedHandler += value; }
            remove { _viewEnvelopeChangedRequestedHandler -= value; }
        }

        event EventHandler<MapViewPropertyChangeEventArgs<Double>>
            IMapView2D.WorldAspectRatioChangeRequested
        {
            add { _worldAspectRatioChangeRequestedHanler += value; }
            remove { _worldAspectRatioChangeRequestedHanler -= value; }
        } 
        #endregion

        #region command handled events
        public event EventHandler ZoomToExtentsRequested;

        public event EventHandler<MapViewPropertyChangeEventArgs<Point2D>> OffsetChangeRequested;

        public event EventHandler<MapViewPropertyChangeEventArgs<Rectangle2D>> ZoomToViewBoundsRequested;

        public event EventHandler<MapViewPropertyChangeEventArgs<BoundingBox>> ZoomToWorldBoundsRequested;

        public event EventHandler<MapViewPropertyChangeEventArgs<Double>> ZoomToWorldWidthRequested;

        public event EventHandler<LocationEventArgs> IdentifyLocationRequested;


        #endregion        
        
        #region Tool Actions

        public event EventHandler<MapActionEventArgs<Point2D>> Hover;

        public event EventHandler<MapActionEventArgs<Point2D>> BeginAction;

        public event EventHandler<MapActionEventArgs<Point2D>> MoveTo;

        public event EventHandler<MapActionEventArgs<Point2D>> EndAction;
  
        #endregion

        #region event notification events

        event EventHandler IMapView2D.SizeChanged
        {
            add { _sizeChangedHandler += value; }
            remove { _sizeChangedHandler -= value; }
        }
        
        #endregion
        #endregion //Events

        public StyleColor BackgroundColor
        {
            get { return _model.BackgroundColor; }
            set { _model.BackgroundColor = value; }
        }

        public Double Dpi
        {
            get{ return _dpi;}
        }
        
        void IMapView2D.Offset(Point2D offsetVector)
        {
            onRequestOffset(offsetVector);
        }

        public Double PixelWorldHeight
        {
            get { return _model.PixelWorldHeight; }
        }

        public Double PixelWorldWidth
        {
            get { return _model.PixelWorldWidth; }
        }

        public ViewSelection2D Selection
        {
            get { return _model.Selection; }
        }

        public void IdentifyLocation(GeoPoint location)
        {
            onRequestIdentifyLocation(location);
        }

        void IMapView2D.ShowRenderedObjects(System.Collections.IEnumerable renderedObjects)
        {
            if (_model != null)
            {
                _model.ShowRenderedObjects(renderedObjects as IEnumerable<Drawing>);
            }
        }

        public Matrix2D ToWorldTransform
        {
            get { return _model.ToWorldTransform; }
        }

        public Point2D ToView(SharpMap.Geometries.Point point)
        {
            return _model.ToView(point);
        }

        public Point2D ToView(Double x, Double y)
        {
            return _model.ToView(x, y);
        }

        public SharpMap.Geometries.Point ToWorld(Point2D point)
        {
            return _model.ToWorld(point);
        }

        public SharpMap.Geometries.Point ToWorld(Double x, Double y)
        {
            return _model.ToWorld(x, y);
        }
 
        public double WorldHeight
        {
            get { return (double)GetValue(WorldHeightProperty); }
            set { SetValue(WorldHeightProperty, value); }
        }
        
        public static readonly DependencyProperty WorldHeightProperty =
            DependencyProperty.Register("WorldHeight", typeof(double), typeof(MapView), new UIPropertyMetadata(default(double)));

        #region WorldWidth

        public static double GetWorldWidth(DependencyObject obj)
        {
            return (double)obj.GetValue(WorldWidthProperty);
        }

        public double WorldWidth
        {
            get { return (double)GetValue(WorldWidthProperty); }
        }

        public static readonly DependencyProperty WorldWidthProperty =
      DependencyProperty.RegisterAttached("WorldWidth", typeof(double), typeof(MapView)
      , new FrameworkPropertyMetadata(default(double), 
         FrameworkPropertyMetadataOptions.Inherits,
          WorldWidthPropertyChanged));

        #endregion        
   
        public Double WorldUnitsPerPixel
        {
            get { return _model.WorldUnitsPerPixel; }
        }

        public void ZoomToExtents()
        {
            MapCommands.ZoomToExtents.Execute(null, this);
        }

        public void ZoomToViewBounds(Rectangle2D viewBounds)
        {
            MapCommands.ZoomToViewBounds.Execute(viewBounds, this);
        }

        public void ZoomToWorldBounds(BoundingBox zoomBox)
        {
            MapCommands.ZoomToWorldBounds.Execute(zoomBox,this);
        }

        public void ZoomToWorldWidth(Double newWorldWidth)
        {
            MapCommands.ZoomToWorldWidth.Execute(newWorldWidth, this);
        }

        #endregion

        //----------------------------------------------------- 
        //
        //  IView Members
        // 
        //-----------------------------------------------------

        #region IView Members

        public System.Boolean Visible
        {
            get
            {
                return this.Visibility == Visibility.Visible;
            }
            set
            {
                this.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        public System.Boolean Enabled
        {
            get
            {
                return this.IsEnabled;
            }
            set
            {
                this.IsEnabled = value;
            }
        }

        public void Hide()
        {
            Visible = false;
        }

        public void Show()
        {
            Visible = true;
        }

        public String Title
        {
            get
            {
                return _title;
            }
            set
            {
                _title = value;
            }
        }

        #endregion


        //------------------------------------------------------
        //
        //  FrameworkElement Members
        //
        //------------------------------------------------------

        #region FrameworkElement Members

        protected override Size MeasureOverride(Size constraint)
        {
            //to avoid infinite view size, we set it to the screen size 
            //which is effectievly the maximal posible view size on the current system
            if (double.IsInfinity(constraint.Width) || double.IsInfinity(constraint.Width))
            {
                ViewSize = new Size2D(SystemParameters.PrimaryScreenWidth, SystemParameters.PrimaryScreenHeight);
            }
            else
            {
                ViewSize = ViewConverter.Convert(constraint);
            }

            constraint = ViewConverter.Convert(ViewSize);

            foreach (FrameworkElement fe in _children)
            {
                fe.Measure(constraint);
            }

            return constraint;
        }

        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            ViewSize = ViewConverter.Convert(arrangeBounds);
            Rect arangeRect = new Rect(arrangeBounds);

            foreach (FrameworkElement fe in _children)
            {
                fe.Arrange(arangeRect);
            }
  
            return ViewConverter.Convert(ViewSize);
        }


        protected override int VisualChildrenCount
        {
            get { return _children.Length; }
        }


        protected override Visual GetVisualChild(int index)
        {
            return _children[index];
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            System.Windows.Point currentPoint = new System.Windows.Point(e.GetPosition(this).X, e.GetPosition(this).Y);
            if (!_dragging && _mouseDownLocation != null && e.LeftButton == MouseButtonState.Pressed)
            {
                _mouseRelativeLocation = new System.Windows.Point(e.GetPosition(this).X - _mouseDownLocation.Value.X, e.GetPosition(this).Y - _mouseDownLocation.Value.Y);
                if (!withinDragTolerance(currentPoint))
                {
                    _dragging = true;
                }
            }
            if (_dragging)
            {
                onMoveTo(ViewConverter.Convert(currentPoint));
            }
            else
            {
                onHover(ViewConverter.Convert(currentPoint));
            }
            _mousePreviousLocation = currentPoint;
            base.OnMouseMove(e);
        }

        protected override void OnMouseUp(MouseButtonEventArgs e)
        {
            base.OnMouseUp(e);
            if (e.ChangedButton == MouseButton.Left)
            {
                onEndAction(ViewConverter.Convert(e.GetPosition(this)));
            }
            _mouseDownLocation = null;
            if (_dragging)
            {
                _dragging = false;
                _mouseRelativeLocation = null;
                _mousePreviousLocation = null;
            }
            Mouse.Capture(null, CaptureMode.SubTree);

        }

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            Mouse.Capture(this, CaptureMode.SubTree);
            _mouseDownLocation = e.GetPosition(this);
            _mousePreviousLocation = _mouseDownLocation;

            this.isMultiselectEnabled = false;

            if (e.ChangedButton == MouseButton.Left) //dragging
            {
                onBeginAction(ViewConverter.Convert(_mouseDownLocation.Value));
            }
        }

        #endregion //FrameworkElement

        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------

        #region Internal Methods
 
        internal MapViewModel ViewModel
        {
            get
            {
                return _model;
            }
        }

        #endregion

        //------------------------------------------------------
        //
        //  Event Invokers
        //
        //------------------------------------------------------

        #region Event Invokers

        private void raiseSizeChanged()
        {
            EventHandler handler = _sizeChangedHandler;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
 
        private void onRequestOffset(Point2D offset)
        {
               MapViewPropertyChangeEventArgs<Point2D> args =
                    new MapViewPropertyChangeEventArgs<Point2D>(Point2D.Zero, offset);
             _model.HandleViewOffsetChangeRequestedWorkoround(this, args);

            //EventHandler<MapViewPropertyChangeEventArgs<Point2D>> e = OffsetChangeRequested;
            //if (e != null)
            //{
            //    MapViewPropertyChangeEventArgs<Point2D> args =
            //        new MapViewPropertyChangeEventArgs<Point2D>(Point2D.Zero, offset);
            //    e(this, args);
            //}
        }

        private void onBeginAction(Point2D actionLocation)
        {
            EventHandler<MapActionEventArgs<Point2D>> e = BeginAction;

            if (e != null)
            {
                _globalActionArgs.SetActionPoint(actionLocation);
                e(this, _globalActionArgs);
            }
        }

        private void onHover(Point2D actionLocation)
        {
            EventHandler<MapActionEventArgs<Point2D>> e = Hover;

            if (e != null)
            {
                _globalActionArgs.SetActionPoint(actionLocation);
                e(this, _globalActionArgs);
            }
        }

        private void onEndAction(Point2D actionLocation)
        {
            EventHandler<MapActionEventArgs<Point2D>> e = EndAction;

            if (e != null)
            {
                _globalActionArgs.SetActionPoint(actionLocation);
                e(this, _globalActionArgs);
            }
         }

        private void onMoveTo(Point2D actionLocation)
        {
            EventHandler<MapActionEventArgs<Point2D>> e = MoveTo;
            if (e != null)
            {
                _globalActionArgs.SetActionPoint(actionLocation);
                e(this, _globalActionArgs);
            }
        }

        private void onRequestIdentifyLocation(GeoPoint location)
        {
            EventHandler<LocationEventArgs> e = IdentifyLocationRequested;

            if (e != null)
            {
                LocationEventArgs args = new LocationEventArgs(location);
                e(this, args);
            }
        }

        private void onRequestZoomToExtents()
        {
            EventHandler e = ZoomToExtentsRequested;
            if (e != null)
            {
                e(this, EventArgs.Empty);
            }
        }

        private void onRequestZoomToViewBounds(Rectangle2D viewBounds)
        {
            EventHandler<MapViewPropertyChangeEventArgs<Rectangle2D>> e = ZoomToViewBoundsRequested;

            if (e != null)
            {
                WpfRect rect = new WpfRect(ViewConverter.Convert(((IMapView2D)this).ViewSize));
                MapViewPropertyChangeEventArgs<Rectangle2D> args =
                    new MapViewPropertyChangeEventArgs<Rectangle2D>(ViewConverter.Convert(rect), viewBounds);

                e(this, args);
            }
        }

        private void onRequestZoomToWorldBounds(BoundingBox zoomBox)
        {
            EventHandler<MapViewPropertyChangeEventArgs<BoundingBox>> e = ZoomToWorldBoundsRequested;
            if (e != null)
            {
                MapViewPropertyChangeEventArgs<BoundingBox> args =
                    new MapViewPropertyChangeEventArgs<BoundingBox>(ViewEnvelope, zoomBox);

                e(this, args);
            }
        }

        private void onRequestZoomToWorldWidth(Double newWorldWidth)
        {
            EventHandler<MapViewPropertyChangeEventArgs<Double>> e = ZoomToWorldWidthRequested;
            if (e != null)
            {
                MapViewPropertyChangeEventArgs<Double> args =
                    new MapViewPropertyChangeEventArgs<Double>(WorldWidth, newWorldWidth);

                e(this, args);
            }
        }

        private static void zoomCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            if (e.Parameter is Double)
            {
                Double factor = (Double)e.Parameter;
                if (factor < 0)
                    NavigationCommands.IncreaseZoom.Execute(factor, This);
                else
                    NavigationCommands.DecreaseZoom.Execute(factor, This);
            }
        }

        //TODO: this handeling should be done in the Tool
        // will be implemnted when the tools support wheel gestures
        private static void increaseZoomCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
             MapView This = (MapView)sender;
            double factor = 1.2;
            if((e.Parameter != null) && 
                typeof(double).IsAssignableFrom(e.Parameter.GetType()))
            {
                factor = (double)e.Parameter;
            }
            This._model.ZoomByFactor(factor);
        }

        static private void decreaseZoomCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
     
            MapView This = (MapView)sender;
            double factor = 0.8;
            if((e.Parameter != null) && 
                typeof(double).IsAssignableFrom(e.Parameter.GetType()))
            {
                factor = (double)e.Parameter;
            }
            This._model.ZoomByFactor(factor);
        }

        #endregion //private

        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------

        #region Private Methods

        Rectangle BackgroundRect
        {
            get { return (Rectangle)_children[0]; }
        }

        DrawingLayer RenderedFeaturesLayer
        {
            get { return (DrawingLayer)_children[1]; }
        }

        DrawingLayer SelectedFeaturesLayer
        {
            get { return (DrawingLayer)_children[2]; }
        }

        DrawingLayer HighlightedFeaturesLayer
        {
            get { return (DrawingLayer)_children[3]; }
        }

        DrawingLayer MapAdornersLayer
        {
            get { return (DrawingLayer)_children[4]; }
        }

        static void WorldWidthPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
        {
            MapView This = target as MapView;
        }
        
        private System.Boolean withinDragTolerance(System.Windows.Point point)
        {
            return Math.Abs(_mouseDownLocation.Value.X - point.X) <= 3 && Math.Abs(_mouseDownLocation.Value.Y - point.Y) <= 3;
        }

        private void WireUpBinding()
        {
            Binding binding = new Binding("Map");
            binding.Mode = BindingMode.OneWay;
            SetBinding(MapView.MapProperty, binding);

            binding = new Binding("GeoCenter");
            binding.Mode = BindingMode.TwoWay;
            SetBinding(MapView.GeoCenterProperty, binding);

            binding = new Binding("WorldWidth");
            binding.Mode = BindingMode.OneWay;
            SetBinding(MapView.WorldWidthProperty, binding);

            binding = new Binding("WorldHeight");
            binding.Mode = BindingMode.OneWay;
            SetBinding(MapView.WorldHeightProperty, binding);

            binding = new Binding("ToViewTransform");
            binding.Mode = BindingMode.OneWay;
            BindingOperations.SetBinding(this, ToViewTransformProperty, binding);

            binding = new Binding("ViewEnvelope");
            binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(this, MapView.ViewEnvelopeProperty, binding);

            binding = new Binding("BackgroundColor");
            binding.Mode = BindingMode.TwoWay;
            binding.Converter = Converters.ColorConverter.Instance;
            BindingOperations.SetBinding(BackgroundRect, Rectangle.FillProperty, binding);

            binding = new Binding("WorldAspectRatio");
            binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(this, MapView.WorldAspectRatioProperty, binding);

            
            binding = new Binding("MaximumWorldWidth");
            binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(this, MapView.MaximumWorldWidthProperty, binding);

            binding = new Binding("MinimumWorldWidth");
            binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(this, MapView.MinimumWorldWidthProperty, binding);
     


            binding = new Binding("Theme");
            binding.Mode = BindingMode.TwoWay;
            BindingOperations.SetBinding(this, MapView.ThemeProperty, binding);
        }

        private static void RegisterCommands()
        {
            InputBinding ib = new InputBinding(NavigationCommands.DecreaseZoom, new MouseWheelGesture(MouseWheelAction.WheelUp));
            CommandManager.RegisterClassInputBinding(typeof(MapView), ib);

            ib = new InputBinding(NavigationCommands.IncreaseZoom, new MouseWheelGesture(MouseWheelAction.WheelDown));
            CommandManager.RegisterClassInputBinding(typeof(MapView), ib);

            CommandBinding zoomBinding = new CommandBinding(NavigationCommands.Zoom);
            zoomBinding.Executed += new ExecutedRoutedEventHandler(zoomCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomBinding);

            CommandBinding zoomIn = new CommandBinding(NavigationCommands.IncreaseZoom);
            zoomIn.Executed += new ExecutedRoutedEventHandler(increaseZoomCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomIn);

            CommandBinding zoomOut = new CommandBinding(NavigationCommands.DecreaseZoom);
            zoomOut.Executed += new ExecutedRoutedEventHandler(decreaseZoomCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomOut);

            CommandBinding offset = new CommandBinding(MapCommands.Offset, OffsetCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), offset);

            CommandBinding zoomToWorldCommand = new CommandBinding(MapCommands.ZoomToWorldBounds, ZoomToWorldBoundsCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomToWorldCommand);

            CommandBinding zoomToViewBounds = new CommandBinding(MapCommands.ZoomToViewBounds, ZoomToViewBoundsCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomToViewBounds);

            CommandBinding zoomToExtents = new CommandBinding(MapCommands.ZoomToExtents, ZoomToExtentsCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomToExtents);

            CommandBinding zoomToWorldWidth = new CommandBinding(MapCommands.ZoomToWorldWidth, ZoomToWorldWidthCommandExecuted);
            CommandManager.RegisterClassCommandBinding(typeof(MapView), zoomToWorldWidth);

        }

        static void OffsetCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            Point2D offset = (Point2D)e.Parameter;
            if (This != null)
            {
                This.onRequestOffset(offset);
            }
        }

        static void ZoomToWorldBoundsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            BoundingBox newEnvelope = (BoundingBox)e.Parameter;
            if (This != null && newEnvelope != null)
            {
                This.onRequestZoomToWorldBounds(newEnvelope);
            }
        }

        static void ZoomToViewBoundsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            Rectangle2D newEnvelope = (Rectangle2D)e.Parameter;
            if (This != null)
            {
                This.onRequestZoomToViewBounds(newEnvelope);
            }
        }

        static void ZoomToWorldWidthCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            double newWorldWidth = (double)e.Parameter;
            if (This != null)
            {
                This.onRequestZoomToWorldWidth(newWorldWidth);
            }
        }

        static void ZoomToExtentsCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            MapView This = (MapView)sender;
            if (This != null )
            {
                This.onRequestZoomToExtents();
            }
        }
        
        private void CreateOwnDrawingLayers()
        {
            _children = new FrameworkElement[] 
            {   
                new Rectangle(), //background
                new DrawingLayer(), //drawing
                new DrawingLayer(), //selected drawing
                new DrawingLayer(), //highlighted drawing
                new DrawingLayer()  //adorners
            };
            foreach (FrameworkElement fe in _children)
            {
                AddVisualChild(fe);
                AddLogicalChild(fe);
            }
        }
        
        
        
        #endregion //private methods

        //------------------------------------------------------
        //
        //  Fields
        //
        //------------------------------------------------------

        #region Fields
        private const Double _dpi = 96;
        private readonly WpfMapActionEventArgs _globalActionArgs = new WpfMapActionEventArgs();
        private MapViewModel _model;
        private String _title = "";

        private System.Windows.Point? _mouseDownLocation;
        private System.Windows.Point? _mousePreviousLocation;
        private System.Windows.Point? _mouseRelativeLocation;
        private System.Boolean _dragging = false;

        FrameworkElement[] _children;

        private bool isMultiselectEnabled = false;
      
        event EventHandler _sizeChangedHandler;
        event EventHandler<MapViewPropertyChangeEventArgs<BoundingBox>> _viewEnvelopeChangedRequestedHandler;
        
        event EventHandler<MapViewPropertyChangeEventArgs<StyleColor>> _backgroundColorChangeRequestedHandler;

        event EventHandler<MapViewPropertyChangeEventArgs<SharpMap.Geometries.Point>> _geoCenterChangeRequestedHandler;
        event EventHandler<MapViewPropertyChangeEventArgs<Double>> _maximumWorldWidthChangeRequestedHandler;
        event EventHandler<MapViewPropertyChangeEventArgs<Double>> _minimumWorldWidthChangeRequestedHandler;
        event EventHandler<MapViewPropertyChangeEventArgs<Double>> _worldAspectRatioChangeRequestedHanler;
        #endregion //fields

    }

     public class WpfMapActionEventArgs : MapActionEventArgs<Point2D>
    {
        public WpfMapActionEventArgs()
            : base(new Point2D())
        {
        }

        public void SetActionPoint(Point2D actionLocation)
        {
            ActionPoint = actionLocation;
        }
    }
}
